package edu.ynu.se.xiecheng.achitectureclass.project.service;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import edu.ynu.se.xiecheng.achitectureclass.project.dao.DeviceRepository;
import edu.ynu.se.xiecheng.achitectureclass.project.dao.GreenhouseRepository;
import edu.ynu.se.xiecheng.achitectureclass.project.dao.SoilDataProcessor;
import edu.ynu.se.xiecheng.achitectureclass.project.dao.SoilDataRepository;
import edu.ynu.se.xiecheng.achitectureclass.project.dto.SoilDataDTO;
import edu.ynu.se.xiecheng.achitectureclass.project.dto.SubTableDataDTO;
import edu.ynu.se.xiecheng.achitectureclass.project.entity.Device;
import edu.ynu.se.xiecheng.achitectureclass.project.entity.Greenhouse;
import edu.ynu.se.xiecheng.achitectureclass.project.entity.SoilData;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.*;
import java.util.stream.Collectors;

@Service
public class SensorDataService {

    @Autowired
    private SoilDataRepository soilDataRepository;

    @Autowired
    private DeviceRepository deviceRepository;

    @Autowired
    private GreenhouseRepository greenhouseRepository;

    @Autowired
    private ObjectMapper objectMapper;
    @Autowired
    private SoilDataProcessor soilDataProcessor;

    @Autowired
    private TokenService tokenService;
    @Autowired
    private DataSource dataSource;
    private final RestTemplate restTemplate = new RestTemplate();

    /**
     * 调用接口获取传感器数据并存储到数据库
     *
     * @param boxNo 大棚编号
     * @param token 访问接口的 Token
     */
    public void fetchAndStoreSensorData(String boxNo, String token) {
        // 获取所有设备 ID
        List<Device> devices = deviceRepository.findAll();

        Map<String, Object> requestBody = new HashMap<>();
        requestBody.put("ids", devices.stream()
                .map(Device::getSerialNumber)
                .collect(Collectors.toList()));

        // 将请求体转换为 JSON 字符串
        String requestBodyJson;
        try {
            requestBodyJson = objectMapper.writeValueAsString(requestBody);
        } catch (JsonProcessingException e) {
            throw new RuntimeException("Failed to convert request body to JSON", e);
        }

        // 打印请求体
        System.out.println("Request Body: " + requestBodyJson);

        String url = "https://fbox360.com/api/v2/dmon/value/get?boxNo=" + boxNo;

        HttpHeaders headers = new HttpHeaders();
        headers.set("Authorization", "Bearer " + token);
        headers.set("Content-Type", "application/json"); // 设置 Content-Type 为 JSON
        System.out.println("Request Headers:");
        headers.forEach((key, value) -> System.out.println(key + ": " + value));

        // 使用 JSON 字符串作为请求体
        HttpEntity<String> entity = new HttpEntity<>(requestBodyJson, headers);

        // 调用接口获取数据
        ResponseEntity<String> response = restTemplate.exchange(url, HttpMethod.POST, entity, String.class);
        String sensorDataJson = response.getBody();

        // 解析并存储数据
        storeSensorData(sensorDataJson, boxNo);
    }

    /**
     * 解析传感器数据并存储到数据库
     *
     * @param jsonData 传感器数据的 JSON 字符串
     * @param boxNo    大棚编号
     */
    private void storeSensorData(String jsonData, String boxNo) {
        try {
            // 解析 JSON 数据为数组
            List<Map<String, Object>> dataList = objectMapper.readValue(jsonData, new TypeReference<List<Map<String, Object>>>() {});

            // 查找或创建大棚
            Greenhouse greenhouse = greenhouseRepository.findByName(boxNo)
                    .orElseGet(() -> {
                        Greenhouse newGreenhouse = new Greenhouse();
                        newGreenhouse.setName(boxNo);
                        newGreenhouse.setAddress("test");
                        newGreenhouse.setArea(100.0);
                        newGreenhouse.setCreateTime(new Date());
                        return greenhouseRepository.save(newGreenhouse);
                    });

            // 存储传感器数据
            for (Map<String, Object> dataMap : dataList) {
                String sensorId = (String) dataMap.get("id");
                Number value = (Number) dataMap.get("value"); // 使用 Number 类型接收值
                String name = (String) dataMap.get("name");

                // 查找设备
                Device device = deviceRepository.findBySerialNumber(sensorId)
                        .orElseThrow(() -> new RuntimeException("Device not found: " + sensorId));

                // 检查是否已经存在相同传感器的数据记录
                Optional<SoilData> existingDataOpt = soilDataRepository.findByDeviceAndName(device, name);

                if (existingDataOpt.isPresent()) {
                    // 如果存在，则更新记录
                    SoilData existingData = existingDataOpt.get();
                    existingData.setValue(value.doubleValue()); // 更新值
                    existingData.setCreateTime(new Date()); // 更新时间
                    soilDataRepository.save(existingData);
                } else {
                    // 如果不存在，则插入新记录
                    SoilData soilData = new SoilData();
                    soilData.setName(name); // 使用传感器名称
                    soilData.setValue(value.doubleValue()); // 转换为 double
                    soilData.setCreateTime(new Date());
                    soilData.setDevice(device);
                    soilDataRepository.save(soilData);
                }
            }
            // 数据存储完成后，调用 SoilDataProcessor 更新子表
            soilDataProcessor.process(); // 调用 process() 方法
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException("Failed to parse and store sensor data", e);
        }
    }

    /**
     * 定时任务：每小时执行一次，自动获取并存储传感器数据
     */
    @Scheduled(fixedRate = 2 * 60 * 60 * 1000) // 60分钟 * 60秒 * 1000毫秒
    public void fetchAndStoreSensorDataHourly() {
        String boxNo = "300822040454"; // 大棚编号
        try {
            // 获取土壤检测设备的 Token
            String token = tokenService.getSoilToken();
            fetchAndStoreSensorData(boxNo, token);
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException("Failed to get soil token", e);
        }
    }
    public List<SoilDataDTO> getAllSoilData() {
        List<SoilData> soilDataList = soilDataRepository.findAll();
        return soilDataList.stream()
                .map(this::convertToDTO)
                .collect(Collectors.toList());
    }

    private SoilDataDTO convertToDTO(SoilData soilData) {
        SoilDataDTO dto = new SoilDataDTO();
        dto.setId(soilData.getId());
        dto.setName(soilData.getName());
        dto.setValue(soilData.getValue());
        dto.setCreateTime(soilData.getCreateTime());
        return dto;
    }
    public List<SubTableDataDTO> getSubTableData(String tableName) {
        String sql = "SELECT * FROM " + tableName;
        List<SubTableDataDTO> dataList = new ArrayList<>();

        try (Connection conn = dataSource.getConnection();
             PreparedStatement stmt = conn.prepareStatement(sql);
             ResultSet rs = stmt.executeQuery()) {

            while (rs.next()) {
                SubTableDataDTO dto = new SubTableDataDTO();
                dto.setId(rs.getLong("id"));
                dto.setCreateTime(rs.getTimestamp("create_time"));
                dto.setValue(rs.getFloat("value"));
                dto.setDeviceId(rs.getInt("device_id"));
                dataList.add(dto);
            }
        } catch (SQLException e) {
            e.printStackTrace();
            throw new RuntimeException("Failed to fetch data from table: " + tableName, e);
        }

        return dataList;
    }
}